package com.hooya.fa.eu.biz.service.impl;

import cn.hutool.core.date.DateField;
import cn.hutool.core.date.DateRange;
import cn.hutool.core.date.DateTime;
import com.alibaba.excel.EasyExcel;
import com.baomidou.dynamic.datasource.annotation.DS;
import com.baomidou.dynamic.datasource.annotation.DSTransactional;
import com.baomidou.mybatisplus.core.incrementer.IdentifierGenerator;
import com.hooya.fa.eu.api.bo.check.CheckProfitBO;
import com.hooya.fa.eu.api.bo.report.*;
import com.hooya.fa.eu.api.constant.FileNameConstant;
import com.hooya.fa.eu.api.enums.BusinessTypeEnum;
import com.hooya.fa.eu.api.enums.CompanyEnum;
import com.hooya.fa.eu.api.util.FileUtil;
import com.hooya.fa.eu.biz.dao.entity.ProfitCheck;
import com.hooya.fa.eu.biz.dao.entity.ProfitCheckDaily;
import com.hooya.fa.eu.biz.dao.entity.ProfitCheckRecord;
import com.hooya.fa.eu.biz.dao.manager.ProfitCheckManager;
import com.hooya.fa.eu.biz.dao.manager.special.InoutReportManager;
import com.hooya.fa.eu.biz.dao.manager.special.StockListManager;
import com.hooya.fa.eu.biz.dao.mapper.ProfitCheckMapper;
import com.hooya.fa.eu.biz.dao.mapper.StkOrigCollectSzzMapper;
import com.hooya.fa.eu.biz.dao.mapper.TempKcjlDailyMapper;
import com.hooya.fa.eu.biz.service.ReportService;
import com.hooya.integrated.common.core.exception.CommonException;
import com.hooya.integrated.common.core.util.BeanCopyUtils;
import com.hooya.integrated.common.util.times.DateExtUtil;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.io.File;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;


/**
 * @Description 1
 * @Author : Zongz
 * @Copyright Copyright (c) 豪雅集团
 * @Create 2023/12/27
 * @Version 1.0.0
 */
@Slf4j
@Service
@DS("fa_eu")
@AllArgsConstructor
public class ReportServiceImpl implements ReportService {

    public static final List<Map<String, String>> inout = new ArrayList<>(2);

    static {
        Map<String, String> mapPl = new HashMap<>();
        mapPl.put("belongs", "costway-fr");
        mapPl.put("country", "FR");
        Map<String, String> mapFr = new HashMap<>();
        mapFr.put("belongs", "costway-pl");
        mapFr.put("country", "PL");
        inout.add(mapPl);
        inout.add(mapFr);
    }

    private final static List<String> incrOrder = Arrays.asList("买", "盘盈", "采购");

    private final IdentifierGenerator identifierGenerator;

    private final StkOrigCollectSzzMapper stkOrigCollectSzzMapper;

    private final InoutReportManager inoutReportManager;

    private final StockListManager stockListManager;

    private final TempKcjlDailyMapper tempKcjlDailyMapper;

    private final ProfitCheckMapper profitCheckMapper;

    private final ProfitCheckManager profitCheckManager;


    @Override
    public void inoutReport(String startDate, String endDate) {
        String yesterday = DateExtUtil.getYesterday(endDate);
        for (Map<String, String> map : inout) {
            List<StkReportInfoBO> fromReportInfoList = new ArrayList<>();
            List<StkReportInfoBO> toReportInfoList = new ArrayList<>();
            String belongs = map.get("belongs");
            String country = map.get("country");
            //采购信息
            List<StkReportInfoBO> cgToList = stkOrigCollectSzzMapper.getBuyListByDate(startDate, endDate, belongs);
            if (!CollectionUtils.isEmpty(cgToList)) {
                toReportInfoList.addAll(cgToList);
            }
            //转运买家
            List<StkReportInfoBO> zyFromList = stkOrigCollectSzzMapper
                    .getTransferFromListByDate(startDate, endDate, belongs, country);
            if (!CollectionUtils.isEmpty(zyFromList)) {
                fromReportInfoList.addAll(zyFromList);
            }
            //转运卖家
            List<StkReportInfoBO> zyToList = stkOrigCollectSzzMapper
                    .getToListByDateAndType(startDate, endDate, belongs, country, BusinessTypeEnum.ZY.getName());
            if (!CollectionUtils.isEmpty(zyToList)) {
                toReportInfoList.addAll(zyToList);
            }
            //b2b买家
            List<StkReportInfoBO> b2bFromList = stkOrigCollectSzzMapper
                    .getB2BFromListByDate(startDate, endDate, belongs, country);
            if (!CollectionUtils.isEmpty(b2bFromList)) {
                fromReportInfoList.addAll(b2bFromList);
            }
            //b2b卖家
            List<StkReportInfoBO> b2bToList = stkOrigCollectSzzMapper
                    .getToListByDateAndType(startDate, endDate, belongs, country, BusinessTypeEnum.B2B.getName());
            if (!CollectionUtils.isEmpty(b2bToList)) {
                toReportInfoList.addAll(b2bToList);
            }
            inoutReportManager.dealWithDetail(fromReportInfoList, belongs, yesterday, "from");
            inoutReportManager.dealWithDetail(toReportInfoList, belongs, yesterday, "to");
            inoutReportManager.generateExport(fromReportInfoList, yesterday, belongs, country, "from");
            inoutReportManager.generateExport(toReportInfoList, yesterday, belongs, country, "to");
        }
    }

    @Override
    public void stockListReport(String startDate, String endDate) {
        //b2c的stk数据
        List<CostwayStkInfoBo> b2cDataList = stkOrigCollectSzzMapper.getB2CListByDate(startDate, endDate);
        //除了b2c的stk数据
        List<CostwayStkInfoBo> excludeB2CDataList = stkOrigCollectSzzMapper.getExcludeB2CListByDate(startDate, endDate);
        //1个月周期的产品数量
        List<CycleNumberBO> cycleNumberList = tempKcjlDailyMapper.getCycleNumberByDate(startDate, endDate);
        Map<String, List<CycleNumberBO>> cycleNumberMap = cycleNumberList.stream()
                .collect(Collectors.groupingBy(l -> l.getSku() + l.getWhId() + l.getBelongs() + l.getDate()));
        b2cDataList.addAll(excludeB2CDataList);
        stockListManager.dealWithStkData(startDate, b2cDataList, cycleNumberMap);
        //补充其他库存数据（本月stock list未使用数据）
        List<CostwayStkInfoBo> otherList =
                tempKcjlDailyMapper.getNotUseBelongsSkuKcList(CompanyEnum.getCostwayList(), startDate, endDate);
        b2cDataList.addAll(otherList);
        Map<String, List<CostwayStkInfoBo>> belongsStkMap = b2cDataList.stream()
                .collect(Collectors.groupingBy(CostwayStkInfoBo::getBelongs));
        stockListManager.generateExport(belongsStkMap, startDate);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void initProfitCheck(String date) {
        List<String> fileNames = new ArrayList<>();
        String dateName = date.replace("-", "");
        fileNames.add("costway-fr_" + dateName + ".csv");
        fileNames.add("costway-it_" + dateName + ".csv");
        fileNames.add("costway-pl_" + dateName + ".csv");
        fileNames.add("costway-de_" + dateName + ".csv");
        for (String fileName : fileNames) {
            profitCheckManager.readAndInsert(fileName, date, identifierGenerator);
        }
    }

    @Override
    @DSTransactional
    public void profitCheckReport(String startDate, String endDate) {
        List<ProfitCheckBO> todayProfitInfo = profitCheckMapper.getListByDate(startDate, endDate);
        if (!CollectionUtils.isEmpty(todayProfitInfo)) {
            throw new RuntimeException("日期区间内已经存在数据，请勿重复生成");
        }
        //初期数据
        String yesterday = DateExtUtil.getYesterday(startDate);
        List<ProfitCheckBO> initialStageList = profitCheckMapper.getListByDateAndBelongs(yesterday, null);
        //所有进仓数据、出仓、结果
        List<ProfitCheckBO> warehousing = new LinkedList<>();
        List<OutBO> outList = new LinkedList<>();
        //将进仓先加，得到本月所有sku数量
        Map<String, List<ProfitCheckBO>> belongsGroupMap = initialStageList.stream()
                .collect(Collectors.groupingBy(ProfitCheckBO::getBelongs));
        List<OutBO> zyOutList = new LinkedList<>();
        List<ProfitCheckBO> checkList = new ArrayList<>(initialStageList);
        //进出数据
        belongsGroupMap.forEach((belongs, init) -> {
            //---------------------进仓---------------------
            List<ProfitCheckBO> buyList = stkOrigCollectSzzMapper
                    .getCgInListByDateAndBelongs(startDate, endDate, belongs);
            List<ProfitCheckBO> inventoryProfitList = stkOrigCollectSzzMapper
                    .getBsInListByDateAndBelongs(startDate, endDate, belongs);
            List<ProfitCheckBO> b2bInList = stkOrigCollectSzzMapper
                    .getB2BInListByDateAndBelongs(startDate, endDate, belongs);
            List<ProfitCheckBO> zyInList = stkOrigCollectSzzMapper
                    .getTransferInListByDateAndBelongs(startDate, endDate, belongs);
            if (!CollectionUtils.isEmpty(buyList)) {
                warehousing.addAll(buyList);
            }
            profitCheckManager.warehousingSku(warehousing, inventoryProfitList);
            profitCheckManager.warehousingSku(warehousing, b2bInList);
            if (!CollectionUtils.isEmpty(zyInList)) {
                warehousing.addAll(zyInList);
                List<OutBO> zyOutData = BeanCopyUtils.copyListProperties(zyInList, OutBO::new, (s, t) -> {
                    t.setOutDate(s.getInboundDate());
                    t.setNum(s.getInbound());
                    t.setBelongs(s.getFlag());
                    t.setType("转运");
                });
                zyOutList.addAll(zyOutData);
                outList.addAll(zyOutData);
            }
            //---------------------出仓---------------------
            List<OutBO> bsOutList = stkOrigCollectSzzMapper.getBsOutList(startDate, endDate, belongs);
            List<OutBO> b2bOutList = stkOrigCollectSzzMapper.getB2bOutList(startDate, endDate, belongs);
            List<OutBO> b2cOutList = stkOrigCollectSzzMapper.getB2cOutList(startDate, endDate, belongs);
            outList.addAll(bsOutList);
            outList.addAll(b2bOutList);
            outList.addAll(b2cOutList);
        });
        checkList.addAll(warehousing);
        //校验转运是否够扣
        zyOutList.forEach(zy -> {
            List<ProfitCheckBO> sellerData = checkList.stream()
                    .filter(l -> l.getBelongs().equals(zy.getBelongs()) && l.getSku().equals(zy.getSku()))
                    .sorted(new TypeComparator().thenComparing(date -> LocalDate.parse(date.getInboundDate(),
                            DateTimeFormatter.ofPattern("yyyy-MM-dd"))))
                    .collect(Collectors.toList());
            if (CollectionUtils.isEmpty(sellerData)) {
                log.error("转运卖家seller数据缺失，卖家货权：" + zy.getBelongs() + "，skuList：" + zy.getSku() + "，数量" + zy.getNum());
                return;
            }
            int remaining = sellerData.stream().mapToInt(ProfitCheckBO::getEndOfTheMonthStock).sum() - zy.getNum();
            if (remaining < 0) {
                log.error("转运数量不够，卖家货权：" + zy.getBelongs() + "，skuList：" + zy.getSku() + "，缺少数量：" + remaining);
            }
        });

        Map<String, List<ProfitCheckBO>> warehousingMap =
                warehousing.stream().collect(Collectors.groupingBy(ProfitCheckBO::getInboundDate));
        Map<String, List<OutBO>> outMap = outList.stream().collect(Collectors.groupingBy(OutBO::getOutDate));
        //每日数据
        List<ProfitCheck> addProfit = new LinkedList<>();
        List<ProfitCheckDaily> addProfitDaily = new LinkedList<>();
        List<ProfitCheckRecord> addProfitRecord = new LinkedList<>();
        DateRange range = DateExtUtil.dateRange(startDate, endDate, DateField.DAY_OF_MONTH);
        LocalDateTime createTime = LocalDateTime.now();
        for (DateTime date : range) {
            String day = date.toDateStr();
            if (day.equals(endDate)) {
                continue;
            }
            //过滤数据，重置状态
            initialStageList = initialStageList.stream().filter(l -> l.getEndOfTheMonthStock() != 0).collect(Collectors.toList());
            initialStageList.forEach(l -> {
                l.setTrackId(null);
                l.setChange(false);
                l.setNumRecord(l.getEndOfTheMonthStock());
            });
            //------------------------- 处理数据 -------------------------
            //进仓
            List<ProfitCheckBO> addList = warehousingMap.get(day);
            if (!CollectionUtils.isEmpty(addList)) {
                for (ProfitCheckBO profit : addList) {
                    //扣减加入减扣数据
                    profit.setChange(true);
                    initialStageList.add(profit);
                    Integer id = identifierGenerator.nextId(new ProfitCheck()).intValue();
                    profit.setId(id);
                    profitCheckManager.genProfitInfo(id, day, addProfit, addProfitDaily, addProfitRecord, profit, createTime,
                            0, profit.getEndOfTheMonthStock(), 0, "");
                }
            }
            //出仓
            List<OutBO> incrList = outMap.get(day);
            if (!CollectionUtils.isEmpty(incrList)) {
                for (OutBO out : incrList) {
                    Integer num = out.getNum();
                    List<ProfitCheckBO> list = initialStageList.stream()
                            .filter(l -> l.getSku().equals(out.getSku()) && l.getEndOfTheMonthStock() > 0 &&
                                    l.getBelongs().equals(out.getBelongs()))
                            .sorted(new TypeComparator().thenComparing(l ->
                                    LocalDate.parse(l.getInboundDate(), DateTimeFormatter.ofPattern("yyyy-MM-dd"))).reversed())
                            .collect(Collectors.toList());
                    if (!CollectionUtils.isEmpty(list)) {
                        //扣减数据，生成新一天得数据，将原始数据切换到新id
                        for (int i = 0; i < list.size(); i++) {
                            ProfitCheckBO bo = list.get(i);
                            Integer startNum = bo.getEndOfTheMonthStock();
                            String type = out.getType();
                            int endNum = startNum - num;
                            if (bo.getChange()) {
                                Integer trackId = bo.getTrackId();
                                if (Objects.isNull(trackId)) {
                                    //进出同日情况，只有change为true，trackId是其本身
                                    trackId = bo.getId();
                                }
                                //之前被减扣过
                                if (endNum >= 0) {
                                    profitCheckManager.deductedAgain(trackId, addProfit, addProfitDaily,
                                            addProfitRecord, endNum, num, type);
                                    bo.setEndOfTheMonthStock(endNum);
                                    break;
                                } else {
                                    profitCheckManager.deductedAgain(trackId, addProfit, addProfitDaily,
                                            addProfitRecord, 0, startNum, type);
                                    num = Math.abs(startNum - num);
                                    bo.setEndOfTheMonthStock(0);
                                }
                            } else {
                                bo.setChange(true);
                                Integer id = identifierGenerator.nextId(new ProfitCheck()).intValue();
                                bo.setBeginOfTheMonthStock(startNum);
                                if (endNum >= 0) {
                                    profitCheckManager.genProfitInfo(id, day, addProfit, addProfitDaily, addProfitRecord,
                                            bo, createTime, startNum, endNum, num, type);
                                    bo.setEndOfTheMonthStock(endNum);
                                    //追踪id（防止数据多次改变，而找不到当前数据设置）
                                    bo.setTrackId(id);
                                    bo.setId(id);
                                    break;
                                } else {
                                    profitCheckManager.genProfitInfo(id, day, addProfit, addProfitDaily, addProfitRecord,
                                            bo, createTime, startNum, 0, startNum, type);
                                    num = Math.abs(startNum - num);
                                    bo.setEndOfTheMonthStock(0);
                                }
                            }
                            if (i == list.size() - 1 && num > 0) {
                                log.error("数量不够被扣减，扣减对象：" + out);
                            }
                        }
                    }
                }
            }
            //未改变数据
            List<ProfitCheckBO> noChangeData = initialStageList.stream().filter(l -> !l.getChange()).collect(Collectors.toList());
            addProfitDaily.addAll(BeanCopyUtils.copyListProperties(noChangeData, ProfitCheckDaily::new, (s, t) -> {
                t.setProfitCheckId(s.getId());
                t.setDate(day);
                t.setNumFlag(false);
                t.setBeginStockNum(s.getEndOfTheMonthStock());
                t.setEndStockNum(s.getEndOfTheMonthStock());
            }));
            //校验
            profitCheckManager.checkDailySl(day, initialStageList);
        }

        //生成excel
        belongsGroupMap.forEach((belongs, res) -> {
            String fileName = belongs + "_" + startDate.substring(0, 7);
            File newFile = FileUtil.createNewFile(FileNameConstant.PROFIT_CHECK_FOLDER_NAME, fileName + ".xlsx");
            EasyExcel.write(newFile.getAbsolutePath(), ProfitCheckBO.class).sheet().doWrite(res);
        });

        profitCheckManager.insertProfit(addProfit, addProfitDaily, addProfitRecord);
    }

    static class TypeComparator implements Comparator<ProfitCheckBO> {
        @Override
        public int compare(ProfitCheckBO o1, ProfitCheckBO o2) {
            int index1 = incrOrder.indexOf(o1.getType());
            int index2 = incrOrder.indexOf(o2.getType());
            return Integer.compare(index1, index2);
        }
    }

    @Override
    public void checkProfitKc(String startDate, String endDate) {
        List<CheckProfitBO> checkProfitList = profitCheckMapper.getCheckProfitList(startDate, endDate);
        List<CheckProfitBO> checkKcList = tempKcjlDailyMapper.getCheckKcList(startDate, endDate);
        Map<String, Integer> kcMap = checkKcList.stream().collect(Collectors.toMap(l ->
                l.getSku() + l.getBelongs() + l.getDate(), CheckProfitBO::getNum));
        for (CheckProfitBO bo : checkProfitList) {
            String key = bo.getSku() + bo.getBelongs() + bo.getDate();
            Integer num = kcMap.get(key);
            CommonException.throwExOnCondition(Objects.isNull(num), "0_0", bo + "库存不存在");
            CommonException.throwExOnCondition(!num.equals(bo.getNum()),
                    "0_0", "数量不对等,库存信息：" + bo + "，利润检查数量：" + num);
        }
        log.info("【" + startDate + " ~ " + endDate + "】" + "利润检查表数据校验通过");
    }
}
